Skip to content

feat: add admin dashboard with account management and service health#24

Merged
technowizard merged 8 commits into
mainfrom
feature/admin-dashboard
Jun 18, 2026
Merged

feat: add admin dashboard with account management and service health#24
technowizard merged 8 commits into
mainfrom
feature/admin-dashboard

Conversation

@technowizard

Copy link
Copy Markdown
Owner

Summary

Adds an admin-only dashboard for account management and operational visibility. New admin route group on the server and a dedicated dashboard-like layout on the web, kept separate from the reading-product shell.

Server authorization is the source of truth; client role is only a navigation affordance.

What's included

Server — admin route group (/admin/*)

  • GET /admin/users — list with limit, offset, status=active|deleted|all, plus per-user articleCount
  • GET /admin/users/:id — detail
  • PATCH /admin/users/:id — update name and role
  • POST /admin/users/:id/reset-password — reset password
  • DELETE /admin/users/:id — soft delete
  • POST /admin/users/:id/restore — restore soft-deleted user
  • GET /admin/health/connections — camoufox browser probe with 5s timeout, returns ok/degraded/down

Every route runs currentUser then adminUser. Demo mode blocks mutations.

Server — auth

  • /auth/user now returns role

Web — admin dashboard

  • AdminLayout: separate chrome (no main navbar/bottom nav), shield mark, title/subtitle, back-to-app link, user menu, section nav
  • Route guard in beforeLoad (server remains authority)
  • Accounts page: status filters, desktop table + mobile cards, per-user article counts
  • Edit dialog: name, role, reset password, delete, restore — with DropdownMenu role selector matching settings pattern
  • Connections panel: 30s auto-refresh, calm status colors
  • Full i18n namespace in en.json

Web — navigation

  • Conditional "Admin" entry in user menu for role === 'admin'

Guards and invariants

  • Last-admin protection on both layers: server blocks demotion/deletion when it would leave zero active admins; UI disables mutations targeting admin-role users
  • Password hashes never appear in any response schema
  • Role is server-validated; client role only gates navigation affordance

Verification

Gate Result
pnpm --filter @loreo/server typecheck passed
pnpm --filter @loreo/web typecheck passed
pnpm --filter @loreo/server test passed — 23 files, 189 tests
pnpm --filter @loreo/web test passed — 22 files, 116 tests
pnpm lint (oxlint + oxfmt) passed — 0 warnings, 0 errors

Manual UI checks (admin/non-admin sessions, desktop/tablet/mobile, keyboard paths, light/dark/sepia) still pending.

- Server /auth/user now returns role for client navigation affordance
- Web auth types map role through get-user, login, register
- Add admin web API modules: list, detail, update, reset password, delete, restore
- Add admin query keys with mutation invalidation metadata
- Server admin authorization remains source of truth
- Add AdminLayout with dashboard-like chrome, shield mark, back-to-app link
- Separate admin route group (_protected/admin) with role guard in beforeLoad
- Add accounts page with status filters and user table (desktop) / cards (mobile)
- Add user edit dialog: name, role, password reset, delete, restore
- Role selector uses DropdownMenu pattern matching settings page
- Disable admin-target mutations (last-admin safety, server guard authoritative)
- Add admin entry to UserMenu for admin role only
- Add table outer border and full i18n namespace for admin copy
…e counts

Phase 4b - service connections:
- Add GET /admin/health/connections admin route
- Add browser.service checkHealth() probe with 5s timeout
- Add admin-health.service aggregating connection checks (camoufox first)
- Break import cycle via lazy import of browser.service
- Add admin connections panel with 30s auto-refresh
- Calm status colors: success/warning/danger, no urgency framing
- Add connections route and nav item

Phase 4c - per-user article counts:
- Add countArticlesByUser() repository method (groupBy aggregation)
- Enrich admin user list response with articleCount
- Add articles column to admin user table (desktop) and mobile card
- Use tabular-nums for stable numeric layout

Verification:
- pnpm --filter @loreo/server typecheck passed
- pnpm --filter @loreo/web typecheck passed
- pnpm --filter @loreo/server exec vitest --run admin files passed: 24 tests
@technowizard technowizard self-assigned this Jun 18, 2026
Login/register routes used selectUsersSchema which omits role. The web client
cached the user via setQueryData with role undefined, and staleTime: Infinity
on useGetUser prevented refetch. This hid the Admin menu entry on the main
page for admin users. Switch login/register response schemas to
currentUserSchema and include role in both handlers.
Locks the contract added in the previous role-exposure fix. Future
regressions that strip role from login/register responses will now fail
the suite instead of silently breaking admin navigation on the web.
@technowizard technowizard merged commit d8d699c into main Jun 18, 2026
2 checks passed
@technowizard technowizard deleted the feature/admin-dashboard branch June 18, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant